"""
认证与鉴权中间件
实现用户认证、权限检查和角色检查等功能
"""
from typing import Callable, List, Union, Annotated
from fastapi import Request, Response, HTTPException, Depends, status
from starlette.middleware.base import BaseHTTPMiddleware
from pkg.common.utils.token.jwt import verify_token
from pkg.common.constants.auth import TokenType
from apps.auth.models.user import User
from apps.auth.services.user_service import UserService
from fastapi.security import OAuth2PasswordBearer
from pkg.settings.config import settings


# 定义权限检查操作符
class CheckOperator:
    AND = "and"  # 与操作，需要满足所有条件
    OR = "or"  # 或操作，满足任意条件即可


class AuthenticationMiddleware(BaseHTTPMiddleware):
    """
    认证中间件
    检查请求头中的Authorization令牌，验证用户是否已认证
    """

    def __init__(self, app, exclude_paths: List[str] = None):
        """
        初始化中间件
        
        Args:
            app: FastAPI应用实例
            exclude_paths: 排除的路径列表，这些路径不需要认证
        """
        super().__init__(app)

        self.exclude_paths = exclude_paths or settings.EXCLUDE_ROUTES
        # 添加API文档路径
        self.exclude_paths.extend(["/api/docs", "/api/redoc", "/openapi.json", "/static/**", ])

    async def dispatch(self, request: Request, call_next: Callable) -> Response:
        # path = request.url.path
        # if any(path.startswith(excluded) for excluded in self.exclude_paths):
        #     return await call_next(request)
        # print(path)

        # 调试输出所有headers
        # print("==== HEADERS ====")
        # for k, v in request.headers.items():
        #     print(f"{k}: {v}")
        # print("=================")

        auth_header = request.headers.get("Authorization")
        # print("Authorization", auth_header)
        # request.state.user = 1

        # 执行请求
        response = await call_next(request)
        # print("response", response)

        return response

    def is_path_excluded(self, path: str) -> bool:
        """
        检查路径是否在排除列表中，支持通配符匹配

        匹配规则:
        - 如果排除路径以 '/**' 结尾: 匹配该路径及其所有子路径
          (如 '/auth/users/**' 匹配 '/auth/users/', '/auth/users/1', '/auth/users/1/2')
        - 如果排除路径以 '/*' 结尾: 匹配该路径及其下一级子路径
          (如 '/auth/users/*' 匹配 '/auth/users/', '/auth/users/1'，但不匹配 '/auth/users/1/2')
        - 否则: 精确匹配路径
          (如 '/auth/login' 只匹配 '/auth/login')
        """
        for pattern in self.exclude_paths:
            # 处理 /** 通配符
            if pattern.endswith("/**"):
                base_path = pattern.rstrip("/**")
                if path.startswith(base_path):
                    # 计算基础路径段数
                    base_segments = len(base_path.rstrip('/').split('/'))
                    path_segments = len(path.rstrip('/').split('/'))

                    # 确保路径是基础路径的子路径
                    if path_segments >= base_segments:
                        return True

            # 处理 /* 通配符
            elif pattern.endswith("/*"):
                base_path = pattern.rstrip("/*")
                if path.startswith(base_path):
                    # 计算基础路径段数
                    base_segments = len(base_path.rstrip('/').split('/'))
                    path_segments = len(path.rstrip('/').split('/'))

                    # 确保路径是基础路径的直接子路径（多一级）
                    if path_segments == base_segments + 1:
                        return True

            # 精确匹配
            else:
                # 规范化路径（忽略末尾斜杠）
                normalized_pattern = pattern.rstrip('/')
                normalized_path = path.rstrip('/')
                if normalized_path == normalized_pattern:
                    return True

        return False


# ====================== 权限和角色检查依赖函数 ======================
oauth2_scheme = OAuth2PasswordBearer(tokenUrl=settings.LOGIN_ROUTE, auto_error=False)


async def get_current_user(request: Request,
                           token: Annotated[str, Depends(oauth2_scheme)] = None
                           ) -> User or None: # type: ignore
    """
    从请求中获取当前用户
    
    两种方式获取用户信息：
    1. 从请求状态(中间件已设置)获取
    2. 通过OAuth2PasswordBearer获取token并验证
    
    Args:
        request: 请求对象
        token: OAuth2PasswordBearer提供的认证令牌
        
    Returns:
        当前认证用户
        
    Raises:
        HTTPException: 如果用户未认证
    """
    # 首先尝试从请求状态中获取用户（中间件设置）
    # user = getattr(request.state, "user", None)
    # print("user", user)

    # 创建中间件实例来检查排除路径
    middleware = AuthenticationMiddleware(None, settings.EXCLUDE_ROUTES)
    path = request.url.path

    # 检查是否为排除路径
    if middleware.is_path_excluded(path):
        return None  # 排除路径不需要认证

    # 如果请求状态中没有用户，但提供了token（通常来自Swagger UI），则验证该token
    if token:
        try:
            # 验证令牌
            user_id = verify_token(token, TokenType.ACCESS)

            if not user_id:
                raise HTTPException(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    detail="无效的认证凭据",
                    headers={"WWW-Authenticate": "Bearer"},
                )

            # 获取用户
            user = await UserService.get_user_by_id(int(user_id))
            if not user:
                raise HTTPException(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    detail="用户不存在",
                    headers={"WWW-Authenticate": "Bearer"},
                )
            request.state.user = user
            return user

        except Exception as e:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail=f"无效的认证凭据: {str(e)}",
                headers={"WWW-Authenticate": "Bearer"},
            )

    # 如果两种方式都不成功，抛出异常
    raise HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="未认证用户",
        headers={"WWW-Authenticate": "Bearer"},
    )


# 检查用户是否为超级管理员
async def require_superuser(user: User = Depends(get_current_user)):
    """
    检查用户是否为超级管理员
    
    Args:
        user: 当前用户
        
    Raises:
        HTTPException: 如果用户不是超级管理员
    """
    if not user.is_superuser:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="需要超级管理员权限"
        )
    return user


# 检查用户是否具有特定权限
def require_permissions(permissions: Union[str, List[str]], operator: str = CheckOperator.AND):
    """
    检查用户是否具有指定权限
    
    Args:
        permissions: 权限标识或权限标识列表
        operator: 操作符，"and"表示必须拥有所有权限，"or"表示拥有任意一个权限即可
        
    Returns:
        依赖函数
    """
    if isinstance(permissions, str):
        permissions = [permissions]

    async def check_permissions(user: User = Depends(get_current_user)):
        # 超级管理员拥有所有权限
        if user.is_superuser:
            return user

        # 检查权限
        results = []
        for permission in permissions:
            has_perm = await user.has_permission(permission)
            results.append(has_perm)

        if operator == CheckOperator.AND and all(results):
            return user
        elif operator == CheckOperator.OR and any(results):
            return user
        else:
            required_perms = ", ".join(permissions)
            op_text = "全部" if operator == CheckOperator.AND else "任意一个"
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail=f"需要{op_text}权限: {required_perms}"
            )

    return check_permissions


# 检查用户是否具有特定角色
def require_roles(roles: Union[str, List[str]], operator: str = CheckOperator.AND):
    """
    检查用户是否具有指定角色
    
    Args:
        roles: 角色标识或角色标识列表
        operator: 操作符，"and"表示必须拥有所有角色，"or"表示拥有任意一个角色即可
        
    Returns:
        依赖函数
    """
    if isinstance(roles, str):
        roles = [roles]

    async def check_roles(user: User = Depends(get_current_user)):
        # 超级管理员拥有所有角色
        if user.is_superuser:
            return user

        # 检查角色
        results = []
        for role in roles:
            has_role = await user.has_role(role)
            results.append(has_role)

        if operator == CheckOperator.AND and all(results):
            return user
        elif operator == CheckOperator.OR and any(results):
            return user
        else:
            required_roles = ", ".join(roles)
            op_text = "全部" if operator == CheckOperator.AND else "任意一个"
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail=f"需要{op_text}角色: {required_roles}"
            )

    return check_roles


# 组合检查：检查角色和权限的组合条件
def require_auth(
        roles: Union[str, List[str]] = None,
        permissions: Union[str, List[str]] = None,
        role_operator: str = CheckOperator.AND,
        permission_operator: str = CheckOperator.AND,
        require_both: bool = True  # True表示同时需要满足角色和权限，False表示满足其中之一即可
):
    """
    复合鉴权：同时检查角色和权限
    
    Args:
        roles: 角色标识或角色标识列表
        permissions: 权限标识或权限标识列表
        role_operator: 角色检查操作符
        permission_operator: 权限检查操作符
        require_both: 是否同时需要满足角色和权限条件
            - True: 需要同时满足角色和权限条件（与操作）
            - False: 只需满足角色或权限条件之一（或操作）
        
    Returns:
        依赖函数
    """
    # 转换单个字符串为列表
    if isinstance(roles, str):
        roles = [roles]
    if isinstance(permissions, str):
        permissions = [permissions]

    async def check_auth(user: User = Depends(get_current_user)):
        # 超级管理员拥有所有权限和角色
        if user.is_superuser:
            return user

        role_result = True
        perm_result = True

        # 检查角色（如果指定了角色）
        if roles:
            role_results = []
            for role in roles:
                has_role = await user.has_role(role)
                role_results.append(has_role)

            if role_operator == CheckOperator.AND:
                role_result = all(role_results)
            else:
                role_result = any(role_results)

        # 检查权限（如果指定了权限）
        if permissions:
            perm_results = []
            for permission in permissions:
                has_perm = await user.has_permission(permission)
                perm_results.append(has_perm)

            if permission_operator == CheckOperator.AND:
                perm_result = all(perm_results)
            else:
                perm_result = any(perm_results)

        # 决定最终授权结果
        if require_both:
            # 需要同时满足角色和权限条件
            if role_result and perm_result:
                return user
        else:
            # 满足角色或权限条件之一即可
            if (not roles or role_result) or (not permissions or perm_result):
                return user

        # 构建错误消息
        error_parts = []
        if roles:
            role_str = ", ".join(roles)
            role_op = "全部" if role_operator == CheckOperator.AND else "任意一个"
            error_parts.append(f"{role_op}角色({role_str})")

        if permissions:
            perm_str = ", ".join(permissions)
            perm_op = "全部" if permission_operator == CheckOperator.AND else "任意一个"
            error_parts.append(f"{perm_op}权限({perm_str})")

        logic = "同时具备" if require_both else "具备其中之一"
        error_msg = f"需要{logic}: {' 和 '.join(error_parts)}"

        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail=error_msg
        )

    return check_auth
